package eclipse.lib.graphics;

import javax.media.opengl.*;
import com.sun.opengl.util.texture.*;
import eclipse.*;
import eclipse.client.Database;
import eclipse.lib.graphics.shaders.GLColor;
import eclipse.lib.graphics.shaders.GLQuadShader;
/** Methods used to Draw 3D objects in OpenGL */
public class GLPrimitives 
{	 	
	public static void drawParticles(GL gl, float size, Texture texture)
	{
		float verts[] = {-size, size, 0f, size, -size, 0f, -size, -size ,0f};
		drawTriad(gl, verts, texture, 0f, 0f, 1f, 1f, 0f, 1f );
	}
	public static void drawText(GL gl, char character)
	{
		character -= 32;		
		int x = character % 16;
		int y = character / 16;
		drawSprite(gl, Database.tFont, x, y, 16);
		gl.glTranslatef(10f, 0f, 0f);
	}
	public static void drawText(GL gl, String text)
	{		
		gl.glPushMatrix();
			for(int s = 0; s < text.length(); s++)
				drawText(gl, text.charAt(s));
		gl.glPopMatrix();
	}	
	/** Specified values for the background */
	public static final float[] skyVerts  = {800f, 600f, -8f};	
	/**
	 * Draws a point to the screen
	 * @param gl OpenGl object
	 * @param x X position of the point
	 * @param y Y position of the point
	 * @param z Z position of the point
	 * @param Color Color of the point
	 * @param u Texture u-value
	 * @param v Texture v-value
	 */
	public static void drawPointf(GL gl, float x, float y, float z, GLColor Color, float u, float v)
	{
		gl.glColor4f(Color.getRed(), Color.getGreen(), Color.getBlue(), Color.getAlpha());
		gl.glTexCoord2f(u, v);
		gl.glVertex3f(x, y, z);	
	}	
	/**
	 * Draws a point to the screen
	 * @param gl OpenGl object
	 * @param x X-position of the point
	 * @param y Y-position of the point
	 * @param z Z-position of the point
	 * @param u U-texture value
	 * @param v V-texture value
	 */
	public static void drawPointf(GL gl, float x, float y, float z, float u, float v)
	{
		gl.glTexCoord2f(u, v);
		gl.glVertex3f(x, y, z);
	}
	/**
	 * Draws a plasma-effect to the screen
	 * @param gl OpenGL object
	 * @param verts Vertices used
	 * @param Color Initial color
	 * @param texture Texture to use
	 * @param inc Incremental value
	 */
	public static void drawPlasmaFilter(GL gl, float[] verts, GLQuadShader Color, Texture texture, float inc)
	{
		Color.setBottomLeft
		(new GLColor
			(
				0.5f * (float)Math.cos(inc * Math.PI / 45) + 0.5f,
				0.5f * (float)Math.sin(inc * Math.PI / 45) + 0.5f,
			    0.5f * (float)Math.cos(inc * Math.PI / 45) + 0.5f,
				0.5f * (float)Math.sin(inc * Math.PI / 60) + 0.5f
			)
		);
		Color.setTopRight
		(new GLColor
			(
			   -0.5f * (float)Math.cos(inc * Math.PI / 60) + 0.5f,
			   -0.5f * (float)Math.sin(inc * Math.PI / 60) + 0.5f,
			   -0.5f * (float)Math.cos(inc * Math.PI / 60) + 0.5f,
				0.5f * (float)Math.sin(inc * Math.PI / 60 + Math.PI / 4) + 0.5f
			)	
		);
		Color.setTopLeft
		(new GLColor
			(
				0.5f * (float)Math.sin(inc * Math.PI / 75) + 0.5f,
			    0.5f * (float)Math.cos(inc * Math.PI / 75) + 0.5f,
			    0.5f * (float)Math.sin(inc * Math.PI / 75) + 0.5f,
				0.5f * (float)Math.sin(inc * Math.PI / 60 + Math.PI / 2) + 0.5f
			)	
		);
		Color.setBottomRight
		(new GLColor
			(
			   -0.5f * (float)Math.sin(inc * Math.PI / 90) + 0.5f,
			   -0.5f * (float)Math.cos(inc * Math.PI / 90) + 0.5f,
			   -0.5f * (float)Math.sin(inc * Math.PI / 90) + 0.5f,
				0.5f * (float)Math.sin(inc * Math.PI / 60 + (Math.PI * 3) / 4) + 0.5f
			)	
		);
		//GLPrimitives.drawQuad(gl, 3f, 2f, -4f, Color, Database.tBLANK);
		GLPrimitives.drawQuad(gl, verts, Color, texture);
	}
	public static void drawTriad(GL gl, float[] verts, Texture texture, float u1, float v1, float u2, float v2, float u3, float v3)	
	{
		texture.bind();
		gl.glEnable(GL.GL_BLEND);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);		
		gl.glBegin(GL.GL_TRIANGLES);
		if(verts.length == 9)
		{
			drawPointf(gl, verts[0], verts[1], verts[2], u1, v1);
			drawPointf(gl, verts[3], verts[4], verts[5], u2, v2);
			drawPointf(gl, verts[6], verts[7], verts[8], u3, v3);
		}
		gl.glEnd();
		gl.glDisable(GL.GL_BLEND);
	}
	/**
	 * Draws a quad to the screen
	 * @param gl OpenGL object
	 * @param verts Vertices to use
	 * @param Color Color to use
	 * @param texture Texture to use
	 * @param u1 Texture Coordinate
	 * @param v1 Texture Coordinate
	 * @param u2 Texture Coordinate
	 * @param v2 Texture Coordinate
	 */
	public static void drawQuad(GL gl, float[] verts, GLQuadShader Color, Texture texture, float u1, float v1, float u2, float v2)
	{
		texture.bind();
		gl.glEnable(GL.GL_BLEND);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE);
		
		gl.glBegin(GL.GL_QUADS);
		{
			if(verts.length == 12)
			{
				drawPointf(gl, verts[0], verts[1], verts[2], Color.getTopLeft(), u1, v1);
				drawPointf(gl, verts[3], verts[4], verts[5], Color.getTopRight(), u2, v1);
				drawPointf(gl, verts[6], verts[7], verts[8], Color.getBottomRight(), u2, v2);
				drawPointf(gl, verts[9], verts[10], verts[11], Color.getBottomLeft(), u1, v2);
			}			
			else if(verts.length == 5)
			{
				drawPointf(gl, verts[0], verts[2], verts[4], Color.getTopLeft(), u1, v1);
				drawPointf(gl, verts[1], verts[2], verts[4], Color.getTopRight(), u2, v1);
				drawPointf(gl, verts[1], verts[3], verts[4], Color.getBottomRight(), u2, v2);
				drawPointf(gl, verts[0], verts[3], verts[4], Color.getBottomLeft(), u1, v2);
			}
			else if(verts.length == 3)
			{
				drawPointf(gl, -verts[0], verts[1], verts[2], Color.getTopLeft(), u1, v1);
				drawPointf(gl, verts[0], verts[1], verts[2], Color.getTopRight(), u2, v1);
				drawPointf(gl, verts[0], -verts[1], verts[2], Color.getBottomRight(), u2, v2);
				drawPointf(gl, -verts[0], -verts[1], verts[2], Color.getBottomLeft(), u1, v2);
			}
		}
		gl.glEnd();
		gl.glDisable(GL.GL_BLEND);		
	}
	/**
	 * Draws a quad to the screen
	 * @param gl OpenGL object
	 * @param verts Vertices to use
	 * @param Color Color to use
	 * @param texture Texture to use
	 */
	public static void drawQuad(GL gl, float[] verts, GLQuadShader Color, Texture texture)
	{
		drawQuad(gl, verts, Color, texture, 0, 0, 1, 1);
	}
	/**
	 * Draws a quad to the screen
	 * @param gl OpenGL object
	 * @param verts Vertices to use
	 * @param tex Texture to use
	 * @param u1 Texture Coordinate
	 * @param v1 Texture Coordinate
	 * @param u2 Texture Coordinate
	 * @param v2 Texture Coordinate
	 */
	public static void drawQuad(GL gl, float[] verts, Texture tex, float u1, float v1, float u2, float v2)
	{
		gl.glEnable(GL.GL_BLEND);
		gl.glEnable(GL.GL_ALPHA_TEST);
		gl.glBlendFunc(GL.GL_SRC_ALPHA, GL.GL_ONE_MINUS_SRC_ALPHA);	  
		gl.glAlphaFunc(GL.GL_GREATER, 0);	  
		tex.bind();
		gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE,
		                                               GL.GL_REPLACE);
		gl.glBegin(GL.GL_QUADS);
		{
			if(verts.length == 12)
			{
				drawPointf(gl, verts[0], verts[1], verts[2], u1, v1);
				drawPointf(gl, verts[3], verts[4], verts[5], u2, v1);
				drawPointf(gl, verts[6], verts[7], verts[8], u2, v2);
				drawPointf(gl, verts[9], verts[10], verts[11], u1, v2);
			}			
			else if(verts.length == 5)
			{
				drawPointf(gl, verts[0], verts[2], verts[4], u1, v1);
				drawPointf(gl, verts[1], verts[2], verts[4], u2, v1);
				drawPointf(gl, verts[1], verts[3], verts[4], u2, v2);
				drawPointf(gl, verts[0], verts[3], verts[4], u1, v2);	
			}
			else if(verts.length == 3)
			{
				drawPointf(gl, -verts[0], verts[1], verts[2], u1, v1);
				drawPointf(gl, verts[0], verts[1], verts[2], u2, v1);
				drawPointf(gl, verts[0], -verts[1], verts[2], u2, v2);
				drawPointf(gl, -verts[0], -verts[1], verts[2], u1, v2);
			}
		}				
		gl.glEnd();
		 
		gl.glTexEnvi(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE,GL.GL_MODULATE);
		//gl.glDisable(GL.GL_ALPHA); // switch off transparency - this shouldnt be here...
		gl.glDisable(GL.GL_BLEND);	
	}
	/**
	 * Draws a quad to the screen
	 * @param gl OpenGL object
	 * @param verts Vertices to use
	 * @param tex Texture to use
	 */
	public static void drawQuad(GL gl, float[] verts, Texture tex)
	{	  
		drawQuad(gl, verts, tex, 0, 0, 1, 1);
	}
	/**
	 * Draws a sprite to the screen
	 * @param gl OpenGl object
	 * @param texture Texture to use
	 * @param xSource Left-most pixel
	 * @param ySource Top-most pixel
	 */
	public static void drawSprite(GL gl, Texture texture, int xSource, int ySource, int frames)
	{
		float x1, x2, y1, y2;
		float verts[] = new float[12];
		float ratioW;
		float ratioH;
		float spriteH = texture.getHeight() / frames;
		float spriteW = texture.getWidth() / frames;
		
		x1 = (xSource * spriteW) / texture.getWidth();
		y1 = (ySource * spriteH) / texture.getHeight();
		x2 = (xSource * spriteW + spriteW) / texture.getWidth();
		y2 = (ySource * spriteH + spriteH) / texture.getHeight();	
		
		ratioW = spriteW / 32f;
		ratioH = spriteH / 32f;
						
		verts[3] = Constants.TILERECT[3] * ratioW;
		verts[6] = Constants.TILERECT[6] * ratioW;
		verts[7] = Constants.TILERECT[7] * ratioH;
		verts[10]= Constants.TILERECT[10]* ratioH;
		drawQuad(gl, verts, texture, x1, y1, x2, y2);
	}
	/**
	 * Draws a tile to the screen
	 * @param gl OpenGL object
	 * @param texture Texture to use
	 * @param xSource Left-most pixel
	 * @param ySource Top-most pixel
	 */
	public static void drawTile(GL gl, Texture texture, int xSource, int ySource)
	{				
		float x1, x2, y1, y2;
		x1 = (xSource * 32f) / texture.getWidth();
		y1 = (ySource * 32f) / texture.getHeight();
		x2 = (xSource * 32f + 32f) / texture.getWidth();
		y2 = (ySource * 32f + 32f) / texture.getHeight();		
		drawQuad(gl, Constants.TILERECT, texture, x1, y1, x2, y2);
	}
	/** 
	 * Draws a skybox to the screen (background)
	 * @param gl OpenGL object
	 * @param texture Texture to use
	 */
	public static void drawSkybox(GL gl, Texture texture)
	{
		int skybox_ID = Database.ID_SKYBOX;
		if(gl.glIsList(skybox_ID) || skybox_ID != 0)
			gl.glCallList(skybox_ID);
		else
		{
			Database.ID_SKYBOX = gl.glGenLists(1);
			gl.glNewList(Database.ID_SKYBOX, GL.GL_COMPILE_AND_EXECUTE);
			{
				drawQuad(gl, skyVerts, texture, 0f, 0f, 1f, 1f);
			}
			gl.glEndList();			
		}		
	}
	public static void rescaleScreen(GL gl, int width, int height)
	{
		gl.glViewport(0, 0, width, height);
	}
	public static void renderToTexture(GL gl, Texture surface, int width, int height)
    {
		surface.bind();
        gl.glCopyTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_LUMINANCE, 0, 0, width, height, 0);
        gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
        gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);      
        rescaleScreen(gl, 640, 480);
    }
	public static void killLayer(GL gl, int layer)
	{
		int layer_ID = Database.ID_LAYER[layer];
		if(gl.glIsList(layer_ID))
			gl.glDeleteLists(layer_ID, 1);
	}
	public static void drawLayer(GL gl, int layer)
	{		
		gl.glPushMatrix();
		for(int i = 0; i < 20; i++)
		{						
			for(int j = 0; j < 15; j++)
			{						
				gl.glPushMatrix();
				{
					gl.glTranslatef(32f * i, 32f * j, 0f);					
					//get cooresponding texture, u, and v values here.
					GLPrimitives.drawTile(gl, Database.tTileSheet[0], 0, 0);
				}
				gl.glPopMatrix();
			}					
		}
		gl.glPopMatrix();
	}
	public static float getY(int Y)
	{
		return (32f * Y);
	}
	public static float getX(int X)
	{
		return (32f * X);
	}
}
